package rowsconverhelp

import (
	"database/sql"
	"errors"
	"reflect"
	"strconv"
	"time"
)

var (
	TIME_TYPE time.Time
	TimeType  = reflect.TypeOf(TIME_TYPE)
)

// reflect.Value写入数据
func setReflectValue(QDestVaule reflect.Value, QSourceValue interface{}) error {
	var vstr string = ""
	switch QDestVaule.Kind() {
	//指针情况下要递归写
	case reflect.Ptr:
		if QSourceValue == nil {
			QDestVaule.Set(reflect.Zero(QDestVaule.Type()))
			return nil
		}
		itemElementP := reflect.New(QDestVaule.Type().Elem())
		QDestVaule.Set(itemElementP)
		return setReflectValue(itemElementP.Elem(), QSourceValue)
	case reflect.String:
		vstr = string(*QSourceValue.(*sql.RawBytes))
		QDestVaule.SetString(vstr)
	case reflect.Bool:
		vstr = string(*QSourceValue.(*sql.RawBytes))
		if vstr == "" {
			vstr = "false"
		}
	case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
		vstr = string(*QSourceValue.(*sql.RawBytes))
		if vstr == "" {
			QDestVaule.SetInt(0)
			return nil
		}
		i64, err := strconv.ParseInt(vstr, 10, QDestVaule.Type().Bits())
		if err != nil {
			return err
		}
		QDestVaule.SetInt(i64)
	case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
		vstr = string(*QSourceValue.(*sql.RawBytes))
		if vstr == "" {
			QDestVaule.SetUint(0)
			return nil
		}
		u64, err := strconv.ParseUint(vstr, 10, QDestVaule.Type().Bits())
		if err != nil {
			return err
		}
		QDestVaule.SetUint(u64)
	case reflect.Float32, reflect.Float64:
		vstr = string(*QSourceValue.(*sql.RawBytes))
		if vstr == "" {
			QDestVaule.SetFloat(0)
			return nil
		}
		f64, err := strconv.ParseFloat(vstr, QDestVaule.Type().Bits())
		if err != nil {
			return err
		}
		QDestVaule.SetFloat(f64)
	case reflect.Array, reflect.Slice:
		QDestVaule.SetBytes(*QSourceValue.(*sql.RawBytes))
	case reflect.Struct:
		//为结构时,时间写入
		if QDestVaule.Type().ConvertibleTo(TimeType) {
			lRawBytes := *QSourceValue.(*sql.RawBytes)
			if lRawBytes != nil {
				vstr = string(lRawBytes)
				ft, err := time.Parse(time.RFC3339, vstr)
				if err != nil {
					return err
				}
				QDestVaule.Set(reflect.ValueOf(ft))
			}
		}
		//其它结构体自行扩展
	default:
		errors.New("未知的字段类型")
	}
	return nil
}

//Rows转arr转构体
//如果数据库取出来的值为null值,那么string默认是''，数字默认是0,布尔型默认是false
// QDestArr可以是 &struct{} 或 &[]*struct{}
// DEMO1  lmytest := &my_test{} rowsconverhelp.RowsToArrStruct(lRows, lmytest)
// DEmo2  lArr := []*my_test{}  rowsconverhelp.RowsToArrStruct(lRows, &lArr)
func RowsToArrStruct(QRows *sql.Rows, QDestArr interface{}) error {
	lDestrv := reflect.ValueOf(QDestArr)
	//首先保证传进来的是地址类型的,才可以进行更改
	if lDestrv.Kind() != reflect.Ptr || lDestrv.IsNil() {
		return errors.New("非指针对象不可转化,当前对像类型:" + reflect.TypeOf(QDestArr).String())
	}
	//指针指定的值
	lDestValue := lDestrv.Elem()
	var lDestItemType reflect.Type
	var lIsStruct bool = false
	switch lDestValue.Kind() {
	case reflect.Slice:
		if lDestValue.Type().Elem().Kind() == reflect.Ptr {
			lDestItemType = lDestValue.Type().Elem().Elem()
		} else {
			lDestItemType = lDestValue.Type().Elem()
		}
	case reflect.Struct:
		lDestItemType = lDestValue.Type()
		lIsStruct = true
	default:
		return errors.New("请传数组对象或结构对象")
	}
	//数组里面的元素非结构体
	if lDestItemType.Kind() != reflect.Struct {
		return errors.New("数组成员只能是结构体")
	}
	lColumnNames, err := QRows.Columns()
	if err != nil {
		return err
	}
	lColumnLen := len(lColumnNames)
	lArrSQLStruct := make([]interface{}, lColumnLen)
	for i := 0; i < lColumnLen; i++ {
		lArrSQLStruct[i] = &sql.RawBytes{}
	}
	//把Rows扫进临时的接收器 lArrSQLStruct
	lZeroValue := reflect.Value{}
	for QRows.Next() {
		err := QRows.Scan(lArrSQLStruct...)
		if err != nil {
			return err
		}
		if lIsStruct {
			for iIndex, itemColumnName := range lColumnNames {
				itemElementField := lDestValue.FieldByName(itemColumnName)
				//找不到此字段
				if itemElementField == lZeroValue || !itemElementField.CanSet() {
					continue
				}
				lerr := setReflectValue(itemElementField, lArrSQLStruct[iIndex])
				if lerr != nil {
					return err
				}
			}
			break
		} else {
			itemElementP := reflect.New(lDestItemType)
			itemElementV := reflect.Indirect(itemElementP)
			for iIndex, itemColumnName := range lColumnNames {
				itemElementField := itemElementV.FieldByName(itemColumnName)
				//找不到此字段
				if itemElementField == lZeroValue || !itemElementField.CanSet() {
					continue
				}
				lerr := setReflectValue(itemElementField, lArrSQLStruct[iIndex])
				if lerr != nil {
					return err
				}
			}
			lDestValue = reflect.Append(lDestValue, itemElementP)
		}
	}
	lDestrv.Elem().Set(lDestValue)
	return nil
}
